package com.example.spider.util;


import cn.hutool.system.OsInfo;
import org.springframework.util.unit.DataSize;

import java.io.BufferedReader;
import java.io.IOException;
import java.time.Duration;
import java.util.Formatter;
import java.util.StringTokenizer;
import java.util.function.Function;

/**
 * 仅支持 linux windows，mac不支持
 *
 * @author lym
 */
public class NetWorkUtil {
    private static final OsInfo OS_INFO = new OsInfo();
    private static final Duration SAMPLING_DURATION = Duration.ofSeconds(1);
    private static final String[] queryNetIOCmd = decideQueryNetIOCmd();
    private static final Function<BufferedReader, DownAndUpBytes> parseCmdFunction = decideParseCmdFunction();
    private static volatile DownAndUpBytes lastRecord = queryNetworkDownUpBytes();
    private static volatile DownAndUpBytes currentRecord = lastRecord;
    private static volatile DownAndUpSpeed currentSpeed = new DownAndUpSpeed(0, 0, Duration.ofSeconds(1));

    static {
        Thread.ofVirtual().name("networkSpeedCalculator").start(() -> {
            while (true) {
                try {
                    Thread.sleep(SAMPLING_DURATION);
                } catch (InterruptedException e) {
                    // ignore
                }
                synchronized (NetWorkUtil.class) {
                    lastRecord = currentRecord;
                    currentRecord = queryNetworkDownUpBytes();
                    currentSpeed = calculateNetworkDownUpSpeed();
                }
            }
        });
    }

    public static DownAndUpSpeed getCurrentSpeed() {
        return currentSpeed;
    }

    public static String[] decideQueryNetIOCmd() {
        // 虽然 mac不支持，但不希望mac跑报错，而是返回当前网速为 0
        return OS_INFO.isLinux() || OS_INFO.isMac() ? new String[]{"ifconfig"} :
                OS_INFO.isWindows() ? new String[]{"netstat", "-e"}
                        : new String[]{"UNKNOWN_SYSTEM--" + OS_INFO.getArch() + "___" + OS_INFO.getName()};
    }

    public static Function<BufferedReader, DownAndUpBytes> decideParseCmdFunction() {
        // 虽然 mac不支持，但不希望mac跑报错，而是返回当前网速为 0
        return OS_INFO.isLinux() || OS_INFO.isMac() ? NetWorkUtil::parseBytesFromUnix :
                OS_INFO.isWindows() ? NetWorkUtil::parseBytesInWindows : r -> {
                    throw new UnsupportedOperationException("unknown system.");
                };
    }

    public static DownAndUpSpeed calculateNetworkDownUpSpeed() {
        Duration duration = Duration.ofMillis(currentRecord.timeStamp - lastRecord.timeStamp);
        return new DownAndUpSpeed(
                currentRecord.down - lastRecord.down,
                currentRecord.up - lastRecord.up,
                duration
        );
    }

    //获取网络上行下行速度 kb/s
//    public static DownAndUpSpeed getNetworkDownUpSpeedKb() {
//        return getNetworkDownUpSpeed(DataSize::toKilobytes);
//    }
//
//    public static String getNetworkDownUpSpeedStr(Function<DataSize, Long> unitConverter) {
//        return getNetworkDownUpSpeed(DataSize::toMegabytes);
//    }

    /**
     * 执行 cmd 获取当前整体网速
     */
    public static DownAndUpBytes queryNetworkDownUpBytes() {
        try (BufferedReader reader = new ProcessBuilder().command(queryNetIOCmd).start().inputReader()) {
            return parseCmdFunction.apply(reader);
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    private static DownAndUpBytes parseBytesFromUnix(BufferedReader input) {
        try {
            long rx = 0, tx = 0;
            String line = null;
            //RX packets:4171603 errors:0 dropped:0 overruns:0 frame:0
            //TX packets:4171603 errors:0 dropped:0 overruns:0 carrier:0
            while ((line = input.readLine()) != null) {
                if (line.contains("RX packets")) {
                    rx += Long.parseLong(line.substring(line.indexOf("RX packets") + 11, line.indexOf(" ", line.indexOf("RX packets") + 11)));
                } else if (line.contains("TX packets")) {
                    tx += Long.parseLong(line.substring(line.indexOf("TX packets") + 11, line.indexOf(" ", line.indexOf("TX packets") + 11)));
                }
            }
            return new DownAndUpBytes(rx, tx, System.currentTimeMillis());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    private static DownAndUpBytes parseBytesInWindows(BufferedReader input) {
        try {
            input.readLine();
            input.readLine();
            input.readLine();
            input.readLine();
            StringTokenizer tokenStat = new StringTokenizer(input.readLine());
            tokenStat.nextToken();
            return new DownAndUpBytes(Long.parseLong(tokenStat.nextToken()), Long.parseLong(tokenStat.nextToken()), System.currentTimeMillis());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static String formatNumber(double f) {
        return new Formatter().format("%.2f", f).toString();
    }

    public record DownAndUpBytes(long down, long up, long timeStamp) {
    }

    public record DownAndUpSpeed(long down, long up, Duration timeStamp) {
        public long getDownloadSpeed(Function<DataSize, Long> unitConverter) {
            return getSpeed(unitConverter, down);
        }

        public long getUploadSpeed(Function<DataSize, Long> unitConverter) {
            return getSpeed(unitConverter, up);
        }

        private long getSpeed(Function<DataSize, Long> unitConverter, long l) {
            return unitConverter.apply(DataSize.ofBytes(l));
        }
    }

    //public static void main(String[] args) throws InterruptedException, IOException {
    //    Thread.sleep(Duration.ofSeconds(3));
    //    System.out.println(currentSpeed.getDownloadSpeed(DataSize::toKilobytes));
    //}
}